package edu.neu.cs6515.net;

import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;

import edu.neu.cs6515.admin.Administrator;
import edu.neu.cs6515.board.HexagonBoard;
import edu.neu.cs6515.player.IPlayer;
import edu.neu.cs6515.player.Player;
import edu.neu.cs6515.player.strategy.StrategyException;
import edu.neu.cs6515.turn.ITurnProxy;
import edu.neu.cs6515.util.Funct;
import edu.neu.cs6515.util.FunctNoRet;
import edu.neu.cs6515.xml.Placement;
import edu.neu.cs6515.xml.Tile;
import edu.neu.cs6515.xml.w12.Accept;
import edu.neu.cs6515.xml.w12.End;
import edu.neu.cs6515.xml.w12.PlayerNames;
import edu.neu.cs6515.xml.w12.Register;
import edu.neu.cs6515.xml.w12.TurnResult;
import edu.neu.cs6515.xml.w12.XMLTurn;

/**
 * the class that serves as a proxy of a remote administrator
 */
public class AdministratorProxy {

    private IProxyInOutput inOut;
    private Player player;

    Map<Class, FunctNoRet<Object>> handlers;

    /**
     * C'tor initialize the AdministratorProxy by IProxyInOutput
     * 
     * @param inOut
     */
    public AdministratorProxy(final IProxyInOutput inOut) {
        super();
        this.inOut = inOut;
        handlers = new HashMap<Class, FunctNoRet<Object>>();
        handlers.put(Accept.class, new AcceptHandler());
        handlers.put(PlayerNames.class, new PlayerNamesHandler());
        handlers.put(XMLTurn.class, new XMLTurnHandler());
        handlers.put(TurnResult.class, new TurnResultHandler());
    }

    /**
     * inform the player of the register accepted
     */
    private class AcceptHandler implements FunctNoRet<Object> {
        @Override
        public void exec(final Object obj) {
            player.registerAccepted(((Accept) obj).getPlayer(),
                    ((Accept) obj).getTile());
        }
    }

    /**
     * inform the player other players names
     */
    private class PlayerNamesHandler implements FunctNoRet<Object> {
        @Override
        public void exec(final Object obj) {
            player.setPlayersInGame(((PlayerNames) obj).toStrings());
        }
    }

    /**
     * inform the player to take your turn
     */
    private class XMLTurnHandler implements FunctNoRet<Object> {
        @Override
        public void exec(final Object obj) {
            XMLTurn xmlTurn = (XMLTurn) obj;
            HexagonBoard hexBoard = new HexagonBoard(xmlTurn.getBoard().getPlayers());
            hexBoard.updateHexagonBoard(xmlTurn.getBoard().getPlacement());
            ITurnProxy turnProxy = new ProxyOfTurnProxy(hexBoard,
                    xmlTurn.getTile(), xmlTurn.getScore(), inOut);
            try {
                player.makeYourTurn(turnProxy);
                inOut.respond(new End());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * inform the player of the turn result
     */
    private class TurnResultHandler implements FunctNoRet<Object> {
        @Override
        public void exec(final Object obj) {
            TurnResult result = (TurnResult) obj;
            player.recieveTurnBroadcast(result.getPlacement(), result.getName());
        }
    }

    /**
     * listen for Administrator's message and take handlers according to the
     * messsage
     */
    public void listenAndRespond() throws InputOutputException {
        while (true) {
            Object obj = inOut.receive();
            System.out.println(player.getPlayersName() + " receives : " + obj);
            handlers.get(obj.getClass()).exec(obj);
        }
    }

    /**
     * register the player to server side administrator
     * 
     * @param name
     *            player's name
     * @param player
     *            player
     */
    public void registerPlayer(final String name, final Player player) throws InputOutputException {
        this.player = player;
        inOut.respond(new Register(name));
        listenAndRespond();
    }

}
